package cn.lg.soar.core.aspect;

import cn.lg.soar.core.annotations.CacheExpire;
import cn.lg.soar.core.annotations.SoarCache;
import cn.lg.soar.core.config.cache.L2CacheManager;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.cache.Cache;
import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.Order;

import java.util.concurrent.CompletableFuture;

/**
 * @author luguoxiang
 * @date 2021/8/24
 * 开源项目：https://gitee.com/lgx1992/lg-soar 求star！请给我star！请帮我点个star！
 */
@Aspect
@Order(0)
public class SoarCacheAspect {

    @Autowired
    private ApplicationContext applicationContext;
    @Autowired
    private L2CacheManager cacheManager;
    @Autowired
    @Qualifier("local")
    private L2CacheManager localCacheManager;
    @Autowired
    @Qualifier("remote")
    private L2CacheManager remoteCacheManager;

    private static final Logger LOGGER = LoggerFactory.getLogger(SoarCacheAspect.class);

    @Around("@annotation(soarCache)")
    public Object around(ProceedingJoinPoint point, SoarCache soarCache) throws Throwable{
        Object value;
        Object key;
        Cache cache;
        // 1.查缓存
        try {
            switch (soarCache.type()) {
                case local:
                    cache = localCacheManager.getCache(
                            soarCache.value(),
                            soarCache.localExpire(),
                            soarCache.remoteExpire()
                    );
                    break;
                case remote:
                    cache = remoteCacheManager.getCache(
                            soarCache.value(),
                            soarCache.localExpire(),
                            soarCache.remoteExpire()
                    );
                    break;
                default:
                    cache = cacheManager.getCache(
                            soarCache.value(),
                            soarCache.localExpire(),
                            soarCache.remoteExpire()
                    );
                    break;
            }
            AopEvaluationContext context = new AopEvaluationContext(point, this.applicationContext, null);
            key = SpelExpressionUtils.getValue(soarCache.key(), context);
            value = cache.get(key, Object.class);
            if (value != null) {
                return value;
            }
        } catch (RuntimeException e) {
            LOGGER.error("获取缓存发生异常", e);
            return point.proceed();
        }

        // 2.调用方法
        value = point.proceed();
        if (value == null) {
            return null;
        }

        // 3.保存缓存
        try {
            Object finalValue = value;
            CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {
                cache.put(key, finalValue);
            });
            // 是否同步
            if (soarCache.sync()) {
                completableFuture.get();
            }
        } catch (RuntimeException e) {
            LOGGER.error("保存缓存发生异常", e);
        }

        return value;
    }

    @Around("@annotation(cacheExpire)")
    public Object around(ProceedingJoinPoint point, CacheExpire cacheExpire) throws Throwable{
        try {
            cacheManager.getCache(cacheExpire.value(), cacheExpire.localExpire(), cacheExpire.remoteExpire());
        } catch (RuntimeException e) {
            LOGGER.error("保存缓存发生异常", e);
        }
        return point.proceed();
    }

}
